
#include "gd32f4xx.h"
#include <stdio.h>


#define RTC_CLOCK_SOURCE_LXTAL
#define BKP_VALUE    0x32F1

#define U8_TO_BCD(x) (((x) / 10 << 4) | ((x) % 10))
// #define U8_TO_BCD(x) (((x) / 10 << 4) | ((x) % 10))

rtc_timestamp_struct rtc_timestamp;
rtc_parameter_struct rtc_initpara;
__IO uint32_t prescaler_a = 0, prescaler_s = 0;
uint32_t RTCSRC_FLAG = 0;

void rtc_setup(void);
void rtc_show_time(void);
void rtc_show_timestamp(void);
void rtc_pre_config(void);

/*!
    \brief      main function
    \param[in]  none
    \param[out] none
    \retval     none
*/
int bs_device_rtc_init(void)
{
    /* enable access to RTC registers in Backup domain */
    rcu_periph_clock_enable(RCU_PMU);
    pmu_backup_write_enable();

    rtc_pre_config();
    /* get RTC clock source selection */
    RTCSRC_FLAG = GET_BITS(RCU_BDCTL, 8, 9);

    /* check if RTC has aready been configured */
    if((BKP_VALUE != RTC_BKP0) || (0x00 == RTCSRC_FLAG)){
        /* backup data register value is not correct or not yet programmed
        or RTC clock source is not configured (when the first time the program 
        is executed or data in RCU_BDCTL is lost due to Vbat feeding) */
        rtc_setup();
    }else{
        /* detect the reset source */
        if (RESET != rcu_flag_get(RCU_FLAG_PORRST)){
            printf("power on reset occurred....\n\r");
        }else if (RESET != rcu_flag_get(RCU_FLAG_EPRST)){
            printf("external reset occurred....\n\r");
        }
        printf("no need to configure RTC....\n\r");

        rtc_show_time();
    }

    rcu_all_reset_flag_clear();

    /* RTC timestamp configuration */
    // rtc_timestamp_enable(RTC_TIMESTAMP_FALLING_EDGE);
    // rtc_interrupt_enable(RTC_INT_TIMESTAMP);
    // rtc_flag_clear(RTC_FLAG_TS|RTC_FLAG_TSOVR);

    //while (1);
    return 0;
}

/*!
    \brief      RTC configuration function
    \param[in]  none
    \param[out] none
    \retval     none
*/
void rtc_pre_config(void)
{
    #if defined (RTC_CLOCK_SOURCE_IRC32K)
          rcu_osci_on(RCU_IRC32K);
          rcu_osci_stab_wait(RCU_IRC32K);
          rcu_rtc_clock_config(RCU_RTCSRC_IRC32K);

          prescaler_s = 0x13F;
          prescaler_a = 0x63;
    #elif defined (RTC_CLOCK_SOURCE_LXTAL)
          rcu_osci_on(RCU_LXTAL);
          rcu_osci_stab_wait(RCU_LXTAL);
          rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);

          prescaler_s = 0xFF;
          prescaler_a = 0x7F;
    #else
    #error RTC clock source should be defined.
    #endif /* RTC_CLOCK_SOURCE_IRC32K */

    rcu_periph_clock_enable(RCU_RTC);
    rtc_register_sync_wait();
}

/*!
    \brief      use hyperterminal to setup RTC time and alarm
    \param[in]  none
    \param[out] none
    \retval     none
*/
void rtc_setup(void)
{
    /* setup RTC time value */
    // uint32_t tmp_hh = 0xFF, tmp_mm = 0xFF, tmp_ss = 0xFF;
    // uint8_t 
    rtc_initpara.factor_asyn = prescaler_a;
    rtc_initpara.factor_syn = prescaler_s;
    rtc_initpara.year = 0x16;
    rtc_initpara.day_of_week = RTC_SATURDAY;
    rtc_initpara.month = RTC_APR;
    rtc_initpara.date = 0x30;
    rtc_initpara.display_format = RTC_24HOUR;
    rtc_initpara.am_pm = RTC_AM;

    /* current time input */
    printf("=======Configure RTC Time========\n\r");
    
    rtc_initpara.hour = 0x10;
    rtc_initpara.minute = 0x10;
    rtc_initpara.second = 0x10;

    /* RTC current time configuration */
    if(ERROR == rtc_init(&rtc_initpara)){
        printf("\n\r** RTC time configuration failed! **\n\r");
    }else{
        printf("\n\r** RTC time configuration success! **\n\r");
        rtc_show_time();
        RTC_BKP0 = BKP_VALUE;
    }
}

/*!
    \brief      display the timestamp time
    \param[in]  none
    \param[out] none
    \retval     none
*/
void rtc_show_timestamp(void)
{
    uint32_t ts_subsecond = 0;
    uint8_t ts_subsecond_ss,ts_subsecond_ts,ts_subsecond_hs ;

    rtc_timestamp_get(&rtc_timestamp);
    /* get the subsecond value of timestamp time, and convert it into fractional format */
    ts_subsecond = rtc_timestamp_subsecond_get();
    ts_subsecond_ss=(1000-(ts_subsecond*1000+1000)/400)/100;
    ts_subsecond_ts=(1000-(ts_subsecond*1000+1000)/400)%100/10;
    ts_subsecond_hs=(1000-(ts_subsecond*1000+1000)/400)%10;

    printf("Get the time-stamp time: %0.2x:%0.2x:%0.2x .%d%d%d \n\r", \
          rtc_timestamp.timestamp_hour, rtc_timestamp.timestamp_minute, rtc_timestamp.timestamp_second,\
          ts_subsecond_ss, ts_subsecond_ts, ts_subsecond_hs);
}


/*!
    \brief      acquire the current time
    \param[in]  none
    \param[out] none
    \retval     none
*/
void rtc_acquire_time(uint8_t *data)
{
    rtc_current_time_get(&rtc_initpara);
    /* get the subsecond value of current time, and convert it into fractional format */
    *data++ = rtc_initpara.year;
    *data++ = rtc_initpara.month;
    *data++ = rtc_initpara.date;
    *data++ = rtc_initpara.hour;
    *data++ = rtc_initpara.minute;
    *data++ = rtc_initpara.second;
    return;
}



/*!
    \brief      display the current time
    \param[in]  none
    \param[out] none
    \retval     none
*/
void rtc_show_time(void)
{
    uint32_t time_subsecond = 0;
    uint8_t subsecond_ss = 0,subsecond_ts = 0,subsecond_hs = 0;

    rtc_current_time_get(&rtc_initpara);
    /* get the subsecond value of current time, and convert it into fractional format */
    time_subsecond = rtc_subsecond_get();
    subsecond_ss=(1000-(time_subsecond*1000+1000)/400)/100;
    subsecond_ts=(1000-(time_subsecond*1000+1000)/400)%100/10;
    subsecond_hs=(1000-(time_subsecond*1000+1000)/400)%10;

    //printf("Current date: %0.2x.%0.2x.%0.2x\n\r",rtc_initpara.year, rtc_initpara.month, rtc_initpara.date);

    printf("Current time: %0.2x:%0.2x:%0.2x .%d%d%d \n\r",rtc_initpara.hour, rtc_initpara.minute, rtc_initpara.second,\
          subsecond_ss, subsecond_ts, subsecond_hs);
}


/*!
    \brief      setup RTC time
    \param[in]  none
    \param[out] none
    \retval     none
*/

int bs_rtc_setup(uint8_t *time_para)
{
    uint8_t temp;
    /* enable access to RTC registers in Backup domain */
    GPIO_BOP(GPIOB) = GPIO_PIN_7;
    rcu_periph_clock_enable(RCU_PMU);
    pmu_backup_write_enable();

    rtc_pre_config();
    /* get RTC clock source selection */
    RTCSRC_FLAG = GET_BITS(RCU_BDCTL, 8, 9);

    rtc_initpara.factor_asyn = prescaler_a;
    rtc_initpara.factor_syn = prescaler_s;
    rtc_initpara.day_of_week = RTC_SATURDAY;
    rtc_initpara.display_format = RTC_24HOUR;
    rtc_initpara.am_pm = RTC_AM;

    /* convert BCD code */
    temp = *time_para++;
    time_para++;
    rtc_initpara.year = U8_TO_BCD(temp);
    temp = *time_para++;
    rtc_initpara.month = U8_TO_BCD(temp);
    temp = *time_para++;
    rtc_initpara.date = U8_TO_BCD(temp);
    temp = *time_para++;
    rtc_initpara.hour = U8_TO_BCD(temp);
    temp = *time_para++;
    rtc_initpara.minute = U8_TO_BCD(temp);
    temp = *time_para++;
    rtc_initpara.second = U8_TO_BCD(temp);

    /* RTC current time configuration */
    if(ERROR == rtc_init(&rtc_initpara)){
        printf("\n\r** RTC time configuration failed! **\n\r");
    }else{
        printf("\n\r** RTC time configuration success! **\n\r");
        rtc_show_time();
        RTC_BKP0 = BKP_VALUE;
    }

    rcu_all_reset_flag_clear();
    return 0;
}


/*!
    \brief      write BKP domain register
    \param[in]  none
    \param[out] none
    \retval     none
*/
void bs_write_bkp_reg(uint8_t reg, uint32_t data)
{
    rcu_periph_clock_enable(RCU_PMU);
    pmu_backup_write_enable();
    REG32((RTC) + 0x50U + reg*4) = data;
}


/*!
    \brief      read BKP domain register
    \param[in]  none
    \param[out] none
    \retval     none
*/
uint32_t bs_read_bkp_reg(uint8_t reg)
{
    uint32_t data;
    if (reg > 19) return 0;
    data = REG32((RTC) + 0x50U + reg*4);
    return data;
}
